package com.web3.management.service;

import com.web3.management.dto.PageResponse;
import com.web3.management.dto.request.TradeLogRequest;
import com.web3.management.dto.response.TradeDailyStatsResponse;
import com.web3.management.dto.response.TradeLogResponse;
import com.web3.management.entity.AccountConfig;
import com.web3.management.entity.TradeLog;
import com.web3.management.exception.BusinessException;
import com.web3.management.repository.AccountConfigRepository;
import com.web3.management.repository.TradeLogRepository;
import com.web3.management.repository.projection.AccountTradeProgressProjection;
import com.web3.management.repository.projection.DailyTradeStatsProjection;
import com.web3.management.security.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 交易日志服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TradeLogService {

    private final TradeLogRepository tradeLogRepository;
    private final AccountService accountService;
    private final AccountConfigRepository accountConfigRepository;
    private final TransactionMonitorService transactionMonitorService;

    @Transactional
    public TradeLogResponse saveLog(@Valid TradeLogRequest request) {
        SecurityUtils.checkAccountAccess(request.getAccountId());
        accountService.getById(request.getAccountId());
        validateAccountConfig(request.getAccountId(), request.getAccountConfigId());

        TradeLog tradeLog = buildEntity(request);
        TradeLog saved = tradeLogRepository.save(tradeLog);

        // v1.3 新增：集成交易监控
        transactionMonitorService.processTransactionMonitor(saved);

        log.info("记录交易日志成功: accountId={}, configId={}, tradeTime={}",
                saved.getAccountId(), saved.getAccountConfigId(), saved.getTradeTime());
        return convertToResponse(saved);
    }

    @Transactional
    public List<TradeLogResponse> saveLogs(List<@Valid TradeLogRequest> requests) {
        if (CollectionUtils.isEmpty(requests)) {
            return new ArrayList<>();
        }
        List<TradeLogResponse> responses = new ArrayList<>();
        for (TradeLogRequest request : requests) {
            responses.add(saveLog(request));
        }
        return responses;
    }

    public PageResponse<TradeLogResponse> queryLogs(Integer accountId,
                                                    Integer accountConfigId,
                                                    LocalDate startDate,
                                                    LocalDate endDate,
                                                    Integer page,
                                                    Integer size) {
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "tradeTime"));
        Specification<TradeLog> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (accountId != null) {
                predicates.add(cb.equal(root.get("accountId"), accountId));
            }
            if (accountConfigId != null) {
                predicates.add(cb.equal(root.get("accountConfigId"), accountConfigId));
            }
            if (startDate != null) {
                predicates.add(cb.greaterThanOrEqualTo(root.get("tradeDate"), startDate));
            }
            if (endDate != null) {
                predicates.add(cb.lessThanOrEqualTo(root.get("tradeDate"), endDate));
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };

        Page<TradeLog> pageData = tradeLogRepository.findAll(specification, pageable);
        List<TradeLogResponse> list = pageData.getContent().stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());
        return PageResponse.of(pageData.getTotalElements(), page, size, list);
    }

    public List<TradeDailyStatsResponse> getDailyStats(Integer accountId,
                                                       Integer accountConfigId,
                                                       LocalDate startDate,
                                                       LocalDate endDate) {
        LocalDate effectiveStart = startDate != null ? startDate : LocalDate.now().minusDays(6);
        LocalDate effectiveEnd = endDate != null ? endDate : LocalDate.now();
        if (effectiveEnd.isBefore(effectiveStart)) {
            throw new BusinessException("结束日期不能早于开始日期");
        }

        List<DailyTradeStatsProjection> projections = tradeLogRepository.aggregateDailyStats(
                accountId, accountConfigId, effectiveStart, effectiveEnd);
        return projections.stream()
                .map(this::convertToDailyStats)
                .collect(Collectors.toList());
    }

    public TradeDailyStatsResponse getSummaryForDate(LocalDate date) {
        List<DailyTradeStatsProjection> projections = tradeLogRepository.aggregateByDate(date);
        if (CollectionUtils.isEmpty(projections)) {
            TradeDailyStatsResponse empty = new TradeDailyStatsResponse();
            empty.setTradeDate(date);
            empty.setPlannedAmount(BigDecimal.ZERO);
            empty.setActualAmount(BigDecimal.ZERO);
            empty.setSlippage(BigDecimal.ZERO);
            return empty;
        }
        return convertToDailyStats(projections.get(0));
    }

    public List<AccountTradeProgressProjection> aggregateAccountSummaryByDate(LocalDate date) {
        return tradeLogRepository.aggregateAccountSummaryByDate(date);
    }

    /**
     * 查询异常交易记录
     */
    public PageResponse<TradeLogResponse> queryAbnormalTransactions(Integer accountId,
                                                                   LocalDate startDate,
                                                                   LocalDate endDate,
                                                                   Integer page,
                                                                   Integer size) {
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "tradeTime"));

        // 构建查询条件
        Specification<TradeLog> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            // 必须是异常交易
            predicates.add(cb.equal(root.get("isAbnormal"), true));

            // 可选的账号筛选
            if (accountId != null) {
                predicates.add(cb.equal(root.get("accountId"), accountId));
            }

            // 可选的日期范围筛选
            if (startDate != null) {
                predicates.add(cb.greaterThanOrEqualTo(root.get("tradeDate"), startDate));
            }
            if (endDate != null) {
                predicates.add(cb.lessThanOrEqualTo(root.get("tradeDate"), endDate));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        };

        Page<TradeLog> pageData = tradeLogRepository.findAll(specification, pageable);
        List<TradeLogResponse> list = pageData.getContent().stream()
                .map(this::convertToResponse)
                .collect(Collectors.toList());

        return PageResponse.of(pageData.getTotalElements(), page, size, list);
    }

    /**
     * 获取监控统计信息
     */
    public Map<String, Object> getMonitorStats(LocalDate date) {
        LocalDate queryDate = date != null ? date : LocalDate.now();

        Map<String, Object> stats = new HashMap<>();
        stats.put("todayTransactions", tradeLogRepository.countByTradeDate(queryDate));
        stats.put("abnormalTransactions", tradeLogRepository.countAbnormalByTradeDate(queryDate));
        stats.put("alertCount", tradeLogRepository.countAlertSentByTradeDate(queryDate));

        Double avgSlippageRate = tradeLogRepository.getAverageSlippageRateByDate(queryDate);
        stats.put("averageSlippage", avgSlippageRate != null ? avgSlippageRate * 100 : 0.0); // 转换为百分比

        return stats;
    }

    private TradeLog buildEntity(TradeLogRequest request) {
        LocalDateTime tradeTime = request.getTradeTime() != null ? request.getTradeTime() : LocalDateTime.now();
        LocalDate tradeDate = tradeTime.toLocalDate();

        BigDecimal planned = request.getPlannedAmount();
        BigDecimal actual = request.getActualAmount();
        BigDecimal slippage = request.getSlippage();
        if (slippage == null && planned != null && actual != null) {
            slippage = planned.subtract(actual);
        }

        return new TradeLog()
                .setAccountId(request.getAccountId())
                .setAccountConfigId(request.getAccountConfigId())
                .setTradeTime(tradeTime)
                .setTradeDate(tradeDate)
                .setDirection(request.getDirection())
                .setPlannedAmount(planned)
                .setActualAmount(actual)
                .setSlippage(slippage)
                .setStatus(request.getStatus())
                .setTxHash(request.getTxHash())
                .setRemark(request.getRemark());
    }

    private TradeLogResponse convertToResponse(TradeLog tradeLog) {
        TradeLogResponse response = new TradeLogResponse();
        response.setId(tradeLog.getId());
        response.setAccountId(tradeLog.getAccountId());
        response.setAccountConfigId(tradeLog.getAccountConfigId());
        response.setTradeDate(tradeLog.getTradeDate());
        response.setTradeTime(tradeLog.getTradeTime());
        response.setDirection(tradeLog.getDirection());
        response.setPlannedAmount(tradeLog.getPlannedAmount());
        response.setActualAmount(tradeLog.getActualAmount());
        response.setSlippage(tradeLog.getSlippage());
        response.setStatus(tradeLog.getStatus());
        response.setTxHash(tradeLog.getTxHash());
        response.setRemark(tradeLog.getRemark());

        // v1.3 新增监控字段
        response.setSlippageRate(tradeLog.getSlippageRate());
        response.setIsAbnormal(tradeLog.getIsAbnormal());
        response.setAlertSent(tradeLog.getAlertSent());

        return response;
    }

    private TradeDailyStatsResponse convertToDailyStats(DailyTradeStatsProjection projection) {
        TradeDailyStatsResponse response = new TradeDailyStatsResponse();
        response.setTradeDate(projection.getTradeDate());
        response.setPlannedAmount(defaultIfNull(projection.getTotalPlannedAmount()));
        response.setActualAmount(defaultIfNull(projection.getTotalActualAmount()));
        response.setSlippage(defaultIfNull(projection.getTotalSlippage()));
        return response;
    }

    private BigDecimal defaultIfNull(BigDecimal value) {
        return value != null ? value : BigDecimal.ZERO;
    }

    private void validateAccountConfig(Integer accountId, Integer accountConfigId) {
        if (accountConfigId == null) {
            return;
        }
        AccountConfig config = accountConfigRepository.findById(accountConfigId)
                .orElseThrow(() -> new BusinessException("账号配置不存在"));
        if (!Objects.equals(config.getAccountId(), accountId)) {
            throw new BusinessException("账号配置与账号不匹配");
        }
    }
}
